﻿//
// *************************************************************************************
// WP-Framework
// 
// Developed by
// Lukas Kretschmar (lkretschmar.dev@bluewin.ch)
//
// Switzerland, Copyright (c) 2013 lkretschmar
// This content is released under the MIT License (http://opensource.org/licenses/MIT).
//     
// *************************************************************************************
//
//



using System;
using System.Linq;
using System.Collections.Generic;
using KretschIT.WP_Fx.Core.Validation;

namespace KretschIT.WP_Fx.EasyMoq.Generator.Items
{
    internal class NamingFactory : INamingFactory
    {
        private const string MockableSuffix = "Mockable";
        private const int GenericNameEndingLength = 2;
        private const string FullNameFormat = "{0}.{1}";
        private const string UniqueNameFormat = "{0}_{1}_";

        private ITypeCollector typesCollector = new TypeCollector();

        internal NamingFactory(IEnumerable<Type> types)
        {
            It.IsNotNull(() => types);

            this.CollectTypes(types);
        }

        private void CollectTypes(IEnumerable<Type> types)
        {
            foreach (var t in types)
            {
                this.typesCollector.Add(t);
            }
        }

        #region MockableName

        private IDictionary<Type, string> uniqueMockableNames = new Dictionary<Type, string>();

        public string CreateMockableNameFor(Type type)
        {
            if (!this.uniqueMockableNames.ContainsKey(type))
            {
                 uniqueMockableNames.Add(type, this.CreateMockableName(type));
            }
            return this.uniqueMockableNames[type] + MockableSuffix;
        }

        private string CreateMockableName(Type type)
        {
            var name = this.GetInterfaceNameOf(type, t => this.GetSimpleInterfaceNameOf(t));
            name = this.RemovePrefix(name);
            name = this.AddSuffix(type, name);
            return this.MakeUnique(name);
        }

        private string GetSimpleInterfaceNameOf(Type type)
        {
            return type.Name;
        }

        private string RemovePrefix(string name)
        {
            if (name.StartsWith("I", StringComparison.CurrentCultureIgnoreCase))
            {
                name = name.Substring(1);
            }
            return name;
        }
        
        private string AddSuffix(Type type, string name)
        {
            if (type.IsGenericType)
            {
                name += type.GetGenericArguments().Count();
            }
            return name;
        }
        
        private string MakeUnique(string name)
        {
            var tempName = name;
            var count = 0;
            while (this.uniqueMockableNames.Values.Any(v => v == tempName))
            {
                tempName = string.Format(UniqueNameFormat, name, ++count);
            }
            return tempName;
        }

        #endregion

        #region NormalizedName

        public string GetNormalizedNameOf(Type type)
        {
            return this.GetInterfaceNameOf(type, t => this.GetUniqueInterfaceNameOf(t));
        }
        
        private string GetUniqueInterfaceNameOf(Type type)
        {
            if (type.IsGenericParameter)
            {
                return type.Name;
            }
            return string.Format(FullNameFormat, type.Namespace, type.Name);
        }

        private string GetInterfaceNameOf(Type type, Func<Type, string> getNameOfType)
        {
            var name = getNameOfType(type);
            if (type.IsGenericType)
            {
                name = this.RemoveGenericSuffix(name);
            }
            return name;
        }

        private string RemoveGenericSuffix(string name)
        {
            var length = name.Length - GenericNameEndingLength;
            return name.Substring(0, length);
        }

        #endregion

        #region UsedTypes

        public IEnumerable<Type> GetUsedTypesOf(Type type)
        {
            return this.typesCollector.GetUsedTypesOf(type);
        }

        #endregion
    }
}
